package com.usdt.common.config;

import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import com.alibaba.fastjson2.JSONObject;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CopyObjectRequest;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.ResponseHeaderOverrides;
import com.usdt.common.core.domain.OssCallbackResult;
import com.usdt.common.core.domain.OssPolicyResult;
import com.usdt.common.enums.FileAccessLevel;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 *
 * </p>
 *
 * @author light
 * @since 2019/1/8
 */
@Service
@Slf4j
public class OssWrapper  {

	@Value("${storage.aliyun.policy.expire}")
	private int ALIYUN_OSS_EXPIRE;
	@Value("${storage.aliyun.maxSize}")
	private int ALIYUN_OSS_MAX_SIZE;
	@Value("${storage.aliyun.callback}")
	private String ALIYUN_OSS_CALLBACK;
	@Value("${storage.aliyun.bucketName}")
	private String ALIYUN_OSS_BUCKET_NAME;
	@Value("${storage.aliyun.endpoint}")
	private String ALIYUN_OSS_ENDPOINT;
	@Value("${storage.aliyun.dir.prefix}")
	private String ALIYUN_OSS_DIR_PREFIX;
	@Value("${storage.aliyun.accessKeyId}")
	private String ACCESS_KEY_ID;
	@Value("${storage.aliyun.host}")
	private String host;

	@Autowired
	private OSSClient ossClient;

	/**
	 * 上传签名
	 * 
	 * @return
	 */
	public OssPolicyResult policy() {

		// 2. 定义oss所需要的数据（callback 由前端设置）
		OssPolicyResult result = new OssPolicyResult();

		// 2.1 生成签证
		String encodedPolicy = null;
		String postSignature = null;
		String policy = generatePostPolicy(ALIYUN_OSS_EXPIRE, ALIYUN_OSS_DIR_PREFIX, 0, ALIYUN_OSS_MAX_SIZE);// 签证
		try {
			encodedPolicy = BinaryUtil.toBase64String(policy.getBytes("utf-8"));
		} catch (UnsupportedEncodingException e) {
			log.error("", e);
		}
		postSignature = ossClient.calculatePostSignature(policy);

		// 2.2 装填oss所需要的数据
		result.setAccessKeyId(ACCESS_KEY_ID); // 相当于账号
		result.setPolicy(encodedPolicy); // 签证信息
		result.setSignature(postSignature); // 可能是oss用来做加密的
		result.setDir(ALIYUN_OSS_DIR_PREFIX); // 所许可的上传路径(前缀)
		result.setHost(host); // oss主机
		result.setExpire(System.currentTimeMillis() + ALIYUN_OSS_EXPIRE); // 许可有效期（应该只是给前端用）
		String key = "doc_file" + System.currentTimeMillis();
		String fileName = Base64Utils.encodeToString(key.getBytes());
		result.setFileName(DateFormatUtils.format(new Date(), "yyyyMMdd") + "/" + fileName);
		log.info("policy:" + JSONObject.toJSONString(result));
		return result;
	}

	/**
	 * 创建上传签证
	 *
	 * @param expiration 签证有效期 ( 单位 ms )
	 * @param prefix     设置许可的文件名（即object-fileKey）径前缀(可以视作允许操作的的文件夹路径)
	 * @param minSize    允许的文件大小下限
	 * @param maxSize    文件大小上限
	 * @return
	 */
	String generatePostPolicy(long expiration, String prefix, long minSize, long maxSize) {
		PolicyConditions policyConds = new PolicyConditions();
		policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, minSize, maxSize);
		policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, prefix);
		String postPolicy = ossClient.generatePostPolicy(new Date(System.currentTimeMillis() + expiration),
				policyConds);
		return postPolicy;
	}

	/**
	 * 上传回调
	 * 
	 * @param request
	 * @return
	 */
	public OssCallbackResult callback(HttpServletRequest request) {
		OssCallbackResult result = new OssCallbackResult();
		String filename = request.getParameter("filename");
		filename = "http://".concat(ALIYUN_OSS_BUCKET_NAME).concat(".").concat(ALIYUN_OSS_ENDPOINT).concat("/")
				.concat(filename);
		result.setFilename(filename);
		result.setSize(request.getParameter("size"));
		result.setMimeType(request.getParameter("mimeType"));
		result.setWidth(request.getParameter("width"));
		result.setHeight(request.getParameter("height"));
		return result;
	}

	public URL getDownloadUrl(String key, String name, Integer isPreview) {
		GeneratePresignedUrlRequest request = getOssRequest(key, name, isPreview);
		return ossClient.generatePresignedUrl(request);
	}

	public URL getDownloadUrl(String fileKey, String fileName) {
		return getDownloadUrl(fileKey, fileName, null);
	}

	public URL getDownloadUrl(String fileKey) {
		return getDownloadUrl(fileKey, null);
	}

	public GeneratePresignedUrlRequest getOssRequest(String fileKey, String browserDisplayName, Integer isPreview) {
		fileKey = StringUtils.removeStart(fileKey, "/");
		GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(ALIYUN_OSS_BUCKET_NAME, fileKey);
		// 告诉oss 响应用户的时候设置一下 响应头 告诉浏览器文件名
		if (StringUtils.isNotEmpty(browserDisplayName)) {
			// new String(fileName.getBytes("utf-8"), "ISO8859-1")
			try {
				String encodeFileName = URLEncoder.encode(browserDisplayName, "UTF-8");
				browserDisplayName = "filename=" + encodeFileName + ";filename*=utf-8''" + encodeFileName;
				browserDisplayName = browserDisplayName.replaceAll("\\+", "%20"); // 空格变+
				request.getResponseHeaders().setContentDisposition("attachment; " + browserDisplayName);
			} catch (UnsupportedEncodingException e) {
				throw new IllegalStateException("将文件名\"" + browserDisplayName + "\"编码为utf-8时发生错误", e);
			}
		}

		request.setExpiration(new Date(System.currentTimeMillis() + ALIYUN_OSS_EXPIRE));
		request.setMethod(HttpMethod.GET);
		
		if (isPreview != null && isPreview == 1) {
			ResponseHeaderOverrides responseHeaders = new ResponseHeaderOverrides();
			responseHeaders.setContentDisposition("inline");
			request.setResponseHeaders(responseHeaders);
		}
		
		return request;
	}


	/**
	 * 创建永久下载链接(取决于文件的acl)
	 * <p>
	 * 注意: 永久链接 不需要签证 所以是匿名访问 理论上在uri上添加参数"response-content-disposition"
	 * 就可以实现指定下载文件名<br>
	 * 但是oss对于这种匿名访问的方式 是不允许附带参数的
	 * <p>
	 * 
	 * @param key
	 * @return
	 */
	public String getPermanentDownloadUrl(String key) {
		String resourcePath = this.host + "/" + OssSdkUtils.urlEncodeKey(key, "utf-8");
		return resourcePath;
	}

	public void setFileAccessLevel(String key, FileAccessLevel accessLevel) {
		ossClient.setObjectAcl(ALIYUN_OSS_BUCKET_NAME, key, CannedAccessControlList.valueOf(accessLevel.name()));
	}

	public void writeFileNameToObjectMeta(String fileKey, String fileName) {
		// 下面的代码是更改对象元信息 附上文档地址
		// https://help.aliyun.com/document_detail/84840.html?spm=a2c4g.11186623.6.749.5ae018c98EH30R
		CopyObjectRequest request = new CopyObjectRequest(ALIYUN_OSS_BUCKET_NAME, fileKey, ALIYUN_OSS_BUCKET_NAME,
				fileKey);
		ObjectMetadata meta = new ObjectMetadata();
		meta.setContentDisposition("attachment;filename=\"" + fileName + "\"");
		request.setNewObjectMetadata(meta);
		ossClient.copyObject(request);
	}

	public String getEndpoint() {
		return ALIYUN_OSS_ENDPOINT;
	}
	
	public String getBucket() {
		return ALIYUN_OSS_BUCKET_NAME;
	}

}
